package com.ebspp.server.storage;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import com.ebspp.global.General;
import com.ebspp.thrift.DataMsg;
import com.ebspp.thrift.Identity;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;
import com.sleepycat.je.LockMode;
import com.sleepycat.je.OperationStatus;
import com.sleepycat.je.Transaction;
import com.sleepycat.persist.EntityCursor;
import com.sleepycat.persist.EntityStore;
import com.sleepycat.persist.PrimaryIndex;
import com.sleepycat.persist.SecondaryIndex;
import com.sleepycat.persist.StoreConfig;
import com.sleepycat.persist.model.Persistent;

public class TxnBDBStore implements IStore
{
	Environment env = null;
	Database db = null;
	TxnDataAccess tda = null;
	
	Identity id;
	String dbDir;
	
	public TxnBDBStore(Identity id) throws ResourceInitException
	{
		this.id = id;
		dbDir = General.dir + "/store/txnBDBStore/" + id;
		File dbDirFile = new File(dbDir);
		if(!dbDirFile.exists())
		{
			if(dbDirFile.mkdirs())
				System.out.println("Creating bdb store dir at: " + dbDir);
			else
				throw new ResourceInitException("Unable to create the db dir at:" + dbDir);
		}
		
		EnvironmentConfig envConf = new EnvironmentConfig();
		envConf.setAllowCreate(true);
		envConf.setTransactional(true);
		env = new Environment(new File(dbDir), envConf);
		
		DatabaseConfig dbConf = new DatabaseConfig();
		dbConf.setAllowCreate(true);
		dbConf.setTransactional(true);
		db = env.openDatabase(null, "txnBDBStore", dbConf);
		
		tda = new TxnDataAccess(db);
	}
	
	
	public boolean write(DataMsg msg, Transaction txn) 
	{
		return tda.put(txn, msg);
	}

	public DataMsg read(DataMsg msg, Transaction txn) 
	{
		// TODO Auto-generated method stub
		return tda.get(txn, msg);
	}

	public boolean delete(DataMsg msg, Transaction txn)
	{
		return tda.delete(txn, msg);
	}
	
	@Override
	public List<DataMsg> bulkWrite(List<DataMsg> data) 
	{
		// TODO Auto-generated method stub
		if(data == null)
			return null;
		
		List<DataMsg> failList = new ArrayList<DataMsg>(0);
		
		Transaction txn = env.beginTransaction(null, null);	
		for(DataMsg msg : data)
		{
			if(!write(msg, txn))
				failList.add(msg);
		}
		if(failList.size() > 0)
			txn.abort();
		
		txn.commit();
		return failList;
	}

	@Override
	public List<DataMsg> bulkRead(List<DataMsg> data) 
	{
		// TODO Auto-generated method stub
		if(data == null)
			return null;
		
		Transaction txn = env.beginTransaction(null, null);
		
		ArrayList<DataMsg> finList = new ArrayList<DataMsg>();
		for(DataMsg msg : data)
			finList.add(read(msg, txn));
		if(finList.size() != data.size())
		{
			txn.abort();
			finList.clear();
		}
		txn.commit();
		
		return finList;
	}
	
	@Override
	public List<DataMsg> garbageCollect(List<DataMsg> data) 
	{
		// TODO Auto-generated method stub
		if(data == null)
			return null;
		
		List<DataMsg> failList = new ArrayList<DataMsg>(0);
		
		Transaction txn = env.beginTransaction(null, null);
		for(DataMsg msg : data)
			if(!delete(msg, txn))
				failList.add(msg);	
		if(failList.size() > 0)
			txn.abort();
		txn.commit();
		
		return failList;
	}
	
	@Override
	public boolean close() 
	{
		// TODO Auto-generated method stub
		try{
			db.close();
			env.close();
		}
		catch(DatabaseException e)
		{
			e.printStackTrace();
			return false;
		}
		return true;
	}
	
	
	class TxnDataAccess
	{
		Database db;
		
		public TxnDataAccess(Database db) throws DatabaseException
		{
			this.db = db;
		}
		
		public boolean put(Transaction txn, DataMsg msg)
		{
			try
			{
				db.put(txn, new DatabaseEntry(msg.getKey().getBytes()), new DatabaseEntry(msg.getValue()));
			}catch(DatabaseException dbe)
			{
				System.err.println("Fail to read:" + msg.getKey());
				return false;
			}
			return true;
		}
		
		public DataMsg get(Transaction txn, DataMsg msg)
		{
			DatabaseEntry value = new DatabaseEntry();
			try
			{
				db.get(txn, new DatabaseEntry(msg.getKey().getBytes()), value	, LockMode.DEFAULT);
				msg.setValue(value.getData());
			}catch(DatabaseException dbe)
			{
				System.err.println("Fail to write:" + msg.getKey());
				return null;
			}
			return msg;
		}
		public boolean delete(Transaction txn, DataMsg msg)
		{
			try
			{
				db.delete(txn, new DatabaseEntry(msg.getKey().getBytes()));
			}catch(DatabaseException dbe)
			{
				System.err.println("Fail to delete:" + msg.getKey());
				return false;
			}
			return true;
		}
	}
	
	public static void main(String[] args) throws ResourceInitException
	{
		TxnBDBStore bdb = new TxnBDBStore(new Identity());
		
		List<DataMsg> msgList = new ArrayList<DataMsg>();
		
		DataMsg msg2 = new DataMsg();
		msg2.setKey("1");
		msg2.setValue("2".getBytes());
		msg2.setTs("1");
		msgList.add(msg2);
		
		DataMsg msg1 = new DataMsg();
		msg1.setKey("1");
		msg1.setValue("1".getBytes());
		msg1.setTs("2");
		msgList.add(msg1);
		
		
		bdb.bulkWrite(msgList);
		
		bdb.bulkRead(msgList);
	}
}
